﻿using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Web.Mvc;

namespace Global.Validation
{
    public class AtLeastOneRequired : ValidationAttribute, IClientModelValidator
    {
        private string _group;

        public AtLeastOneRequired()
        {

        }

        public AtLeastOneRequired(string group)
        {
            _group = group;
        }

        protected override ValidationResult IsValid(
            object value,
            ValidationContext validationContext)
        {
            bool atLeastOneNotNull =

                //all properties 
                validationContext.ObjectType.GetProperties()

                //with Group attributes
                .Where(p => p.IsDefined(typeof(AtLeastOneRequired), true))

                //only in current group
                .Where(p => p.GetCustomAttributesData().Any(
                    a => a.ConstructorArguments.Count > 0 &&
                         a.ConstructorArguments.Any(aca => aca.Value.ToString() == _group))
                )

                //at least one is not null
                .Any(p => p.GetValue(validationContext.ObjectInstance, null) != null)

                ;

            return atLeastOneNotNull ?
                null :
                new ValidationResult(ErrorMessage);
        }

        /// <summary>
        /// ONLY for test use by setting it as public
        /// </summary>
        public ValidationResult IsValidForTest(
            object value,
            ValidationContext validationContext)
        {
            return IsValid(value, validationContext);
        }

        public void AddValidation(ClientModelValidationContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            MergeAttribute(context.Attributes, "data-val", "true");
            MergeAttribute(context.Attributes, "data-val-atleastonerequire", ErrorMessage);
            MergeAttribute(context.Attributes, "data-val-atleastonerequire-group", _group);
        }

        private bool MergeAttribute(IDictionary<string, string> attributes, string key, string value)
        {
            if (attributes.ContainsKey(key))
            {
                return false;
            }
            attributes.Add(key, value);
            return true;
        }
    }
}
